﻿Imports System.IO
Imports System.Security.Cryptography.X509Certificates

Public Class csCertificadosSAT
    Private rutaOpenSSL As String = ""
    Public Enum tipoCertificado
        CSD
        FIEL
        OTRO
    End Enum

    ''' <summary>
    ''' Constructor de la Clase
    ''' </summary>
    ''' <param name="pRutaOpenSSL">Ruta donde se debe encontrar los archivos auxiliares de Openssl</param>
    ''' <remarks>Sin la diagonal al final</remarks>
    Public Sub New(ByVal pRutaOpenSSL As String)
        Me.rutaOpenSSL = pRutaOpenSSL
    End Sub

    ''' <summary>
    ''' Obtiene la información del Certificado que se encuentra en la ruta pRutaCert
    ''' </summary>
    ''' <param name="pRutaCert">Ruta completa donde se encuentra el archivo *.CER del que se va a obtener informacion</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Shared Function getInfoCertificado(ByVal pRutaCert As String, ByRef pStrRFCSujeto As String) As String
        Dim objCert As X509Certificate2
        Dim DatoHex As String
        Dim Data1 As String
        Dim Resultado As String
        Dim strInfo As String
        Dim strEmitPor As String
        Dim strEmitPara As String
        Dim strSubjectName As String()
        pStrRFCSujeto = ""
        Try
            strInfo = ""
            strEmitPor = ""
            strEmitPara = ""
            objCert = New X509Certificate2(pRutaCert)
            DatoHex = objCert.SerialNumber
            Data1 = ""
            Resultado = ""

            While (DatoHex.Length > 0)
                Data1 = System.Convert.ToChar(System.Convert.ToUInt32(DatoHex.Substring(0, 2), 16)).ToString
                Resultado = Resultado & Data1
                DatoHex = DatoHex.Substring(2, DatoHex.Length - 2)
            End While

            strEmitPor = objCert.Issuer.Substring(objCert.Issuer.LastIndexOf("CN=") + 3)
            strEmitPara = objCert.Subject.Substring(objCert.Subject.LastIndexOf("CN=") + 3)
            strSubjectName = objCert.Subject.Split(New Char() {","c})
            For Each str As String In strSubjectName
                If (str.Trim.StartsWith("OID.2.5.4.45")) Then
                    Dim index As Int16 = -1
                    index = str.IndexOf("=") + 1
                    If (str.Substring(index, 1) = """") Then
                        index = index + 1
                    End If
                    pStrRFCSujeto = str.Substring(index, 13).Trim.ToUpper()
                    index = Nothing
                    Exit For
                End If
            Next

            strInfo = String.Format("Emitido por: {0}{1}Emitido para: {2}{3}Num. Serie: {4}{5}Válido desde: {6}{7}Válido hasta: {8}{9}", _
                                    strEmitPor, vbCrLf, strEmitPara, vbCrLf, Resultado, vbCrLf, objCert.NotBefore, vbCrLf, objCert.NotAfter, vbCrLf)
            getInfoCertificado = strInfo
        Catch ex As Exception
            Log.LogException(ex, "csCertificadosSAT.getInfoCertificado")
            getInfoCertificado = "NO SE PUDO LEER EL CERTIFICADO:" & vbCrLf & ex.Message
            pStrRFCSujeto = ""
        Finally
            objCert = Nothing
            DatoHex = Nothing
            Data1 = Nothing
            Resultado = Nothing
            strInfo = Nothing
            strEmitPara = Nothing
            strEmitPor = Nothing
            strSubjectName = Nothing
        End Try
    End Function

    ''' <summary>
    ''' Valida si un certificado proporcionado corresponde a un CSD o a una Fiel en base a los Usos de la Clave, la cadena OU del sujeto
    ''' </summary>
    ''' <param name="pRutaCert">Ruta absoluta del Certificado a evaluar</param>
    ''' <returns>
    ''' True: Si el certificado corresponde a un CSD
    ''' False: Si es una Fiel o no se puede determinar
    ''' </returns>
    ''' <remarks></remarks>
    Public Shared Function esCSD(ByVal pRutaCert As String) As Boolean
        Dim objCert As New X509Certificate2(pRutaCert)
        Dim strOid As String = "2.5.29.15"
        Dim strFriendlyName As String = "Uso de la clave"
        Dim keyUsageExtension As X509KeyUsageExtension = CType(objCert.Extensions(strOid), X509KeyUsageExtension)
        Dim hitsEsCsd As Int16 = 0
        Dim hitsEsFiel As Int16 = 0

        Try
            'Se evaluan las extensiones de uso de clave:
            'Fiel tiene 4
            'CSD tiene  2
            If (keyUsageExtension IsNot Nothing) Then
                'MsgBox(String.Format("key usage is 0x{0:x4}, {1}", CInt(keyUsageExtension.KeyUsages), keyUsageExtension.KeyUsages))
                If (String.Format("{0:x}", CInt(keyUsageExtension.KeyUsages)).ToUpper() = "E8" Or _
                    String.Format("{0:x}", CInt(keyUsageExtension.KeyUsages)).ToUpper() = "D8") Then
                    hitsEsFiel = hitsEsFiel + 1
                End If
                If (String.Format("{0:x}", CInt(keyUsageExtension.KeyUsages)).ToUpper() = "C0") Then
                    hitsEsCsd = hitsEsCsd + 1
                End If
                If ((keyUsageExtension.KeyUsages And X509KeyUsageFlags.DigitalSignature) = X509KeyUsageFlags.DigitalSignature) Then
                    hitsEsFiel = hitsEsFiel + 1
                    hitsEsCsd = hitsEsCsd + 1
                End If
                If ((keyUsageExtension.KeyUsages And X509KeyUsageFlags.NonRepudiation) = X509KeyUsageFlags.NonRepudiation) Then
                    hitsEsFiel = hitsEsFiel + 1
                    hitsEsCsd = hitsEsCsd + 1
                End If
                If ((keyUsageExtension.KeyUsages And X509KeyUsageFlags.DataEncipherment) = X509KeyUsageFlags.DataEncipherment) Then
                    hitsEsFiel = hitsEsFiel + 1
                End If
                If ((keyUsageExtension.KeyUsages And X509KeyUsageFlags.KeyAgreement) = X509KeyUsageFlags.KeyAgreement) Then
                    hitsEsFiel = hitsEsFiel + 1
                End If
            End If
            'Se evalua si en el sujeto, la cadena empieza con "OU="
            If (objCert.Subject.StartsWith("OU=")) Then
                hitsEsCsd = hitsEsCsd + 1
            Else
                hitsEsFiel = hitsEsFiel + 1
            End If

            'Se evalua cual tiene mayor peso
            If (hitsEsFiel > hitsEsCsd) Then
                'ES FIEL
                Return False
            ElseIf (hitsEsFiel < hitsEsCsd) Then
                'ES CSD
                Return True
            ElseIf (hitsEsFiel = hitsEsCsd) Then
                Return False
            End If
        Catch ex As Exception
            Log.LogException(ex, "csCertificadosSAT.esCSD")
            Return False
        Finally
            objCert = Nothing
            strOid = Nothing
            strFriendlyName = Nothing
            keyUsageExtension = Nothing
            hitsEsCsd = Nothing
            hitsEsFiel = Nothing
        End Try
    End Function

    Public Shared Function getTipoCertificado(ByVal pRutaCert As String) As tipoCertificado
        Dim objCert As New X509Certificate2()
        Dim strOid As String = "2.5.29.15"
        Dim keyUsageExtension As X509KeyUsageExtension
        Dim hitsEsCsd As Int16 = 0
        Dim hitsEsFiel As Int16 = 0

        Try
            objCert = New X509Certificate2(pRutaCert)
            keyUsageExtension = CType(objCert.Extensions(strOid), X509KeyUsageExtension)
            'Se evaluan las extensiones de uso de clave:
            'Fiel tiene 4
            'CSD tiene  2
            If (keyUsageExtension IsNot Nothing) Then
                'MsgBox(String.Format("key usage is 0x{0:x4}, {1}", CInt(keyUsageExtension.KeyUsages), keyUsageExtension.KeyUsages))
                If (String.Format("{0:x}", CInt(keyUsageExtension.KeyUsages)).ToUpper() = "E8" Or _
                    String.Format("{0:x}", CInt(keyUsageExtension.KeyUsages)).ToUpper() = "D8") Then
                    hitsEsFiel = hitsEsFiel + 1
                End If
                If (String.Format("{0:x}", CInt(keyUsageExtension.KeyUsages)).ToUpper() = "C0") Then
                    hitsEsCsd = hitsEsCsd + 1
                End If
                If ((keyUsageExtension.KeyUsages And X509KeyUsageFlags.DigitalSignature) = X509KeyUsageFlags.DigitalSignature) Then
                    hitsEsFiel = hitsEsFiel + 1
                    hitsEsCsd = hitsEsCsd + 1
                End If
                If ((keyUsageExtension.KeyUsages And X509KeyUsageFlags.NonRepudiation) = X509KeyUsageFlags.NonRepudiation) Then
                    hitsEsFiel = hitsEsFiel + 1
                    hitsEsCsd = hitsEsCsd + 1
                End If
                If ((keyUsageExtension.KeyUsages And X509KeyUsageFlags.DataEncipherment) = X509KeyUsageFlags.DataEncipherment) Then
                    hitsEsFiel = hitsEsFiel + 1
                End If
                If ((keyUsageExtension.KeyUsages And X509KeyUsageFlags.KeyAgreement) = X509KeyUsageFlags.KeyAgreement) Then
                    hitsEsFiel = hitsEsFiel + 1
                End If
            End If
            'Se evalua si en el sujeto, la cadena empieza con "OU="
            If (objCert.Subject.StartsWith("OU=")) Then
                hitsEsCsd = hitsEsCsd + 1
            Else
                hitsEsFiel = hitsEsFiel + 1
            End If

            'Se evalua cual tiene mayor peso
            If (hitsEsFiel > hitsEsCsd) Then
                Return tipoCertificado.FIEL
            ElseIf (hitsEsFiel < hitsEsCsd) Then
                Return tipoCertificado.CSD
            Else
                Return tipoCertificado.OTRO
            End If

        Catch ex As Exception
            Log.LogException(ex, "csCertificadosSAT.getTipoCertificado")
            Return tipoCertificado.OTRO
        Finally
            objCert = Nothing
            strOid = Nothing
            keyUsageExtension = Nothing
            hitsEsCsd = Nothing
            hitsEsFiel = Nothing
        End Try
    End Function

    ''' <summary>
    ''' Obtiene el número de serie de 20 caracteres del certificado
    ''' </summary>
    ''' <param name="pCert">archivo de Certificado de Sello Digital (*.cer)</param>
    ''' <returns>Cadena de 20 caracteres que representan el numero de serie del Certificado, o 
    ''' Cadena vacía en caso de que no se haya podido obtener</returns>
    ''' <remarks></remarks>
    Public Shared Function getSerieCertificado(ByVal pCert As Byte(), ByRef fIniVig As Date, ByRef fFinVig As Date, ByRef strRfcSujeto As String) As String
        Dim objCert As New X509Certificate2()
        Dim DatoHex As String = ""
        Dim Data1 As String = ""
        Dim strNoSerie As String = ""
        strRfcSujeto = ""
        Try
            objCert = New X509Certificate2(pCert)
            DatoHex = objCert.SerialNumber
            While (DatoHex.Length > 0)
                Data1 = System.Convert.ToChar(System.Convert.ToUInt32(DatoHex.Substring(0, 2), 16)).ToString
                strNoSerie = strNoSerie & Data1
                DatoHex = DatoHex.Substring(2, DatoHex.Length - 2)
            End While

            If (strNoSerie.Length <> 20) Then
                strNoSerie = ""
            End If

            For Each str As String In objCert.Subject.Split(New Char() {","c})
                If (str.Trim.StartsWith("OID.2.5.4.45")) Then
                    Dim index As Int16 = -1
                    index = str.IndexOf("=") + 1
                    If (str.Substring(index, 1) = """") Then
                        index = index + 1
                    End If
                    strRfcSujeto = str.Substring(index, 13).Trim.ToUpper()
                    index = Nothing
                    Exit For
                End If
            Next

            fIniVig = objCert.NotBefore
            fFinVig = objCert.NotAfter

            Return strNoSerie
        Catch ex As Exception
            Log.LogException(ex, "csCertificadosSAT.getSerieCertificado")
            Return ""
        Finally
            objCert = Nothing
            DatoHex = Nothing
            Data1 = Nothing
            strNoSerie = Nothing
        End Try
    End Function

    ''' <summary>
    ''' Valida si el Certificado es vigente respecto a la fecha y hora del sistema
    ''' </summary>
    ''' <param name="pRutaCert">Ruta completa del archivo de certificado de sello digital (*.cer)</param>
    ''' <returns>
    ''' True: Si la fecha actual esta dentro del rango de vigencia del certificado
    ''' False: En caso contrario
    ''' </returns>
    ''' <remarks></remarks>
    Public Shared Function esCertificadoVigente(ByVal pRutaCert As String, ByVal pFechaValidacion As Date) As Boolean
        Dim objCert As New X509Certificate2()
        Try
            objCert = New X509Certificate2(pRutaCert)
            If (pFechaValidacion > objCert.NotBefore And pFechaValidacion < objCert.NotAfter) Then
                Return True
            Else
                Return False
            End If
        Catch ex As Exception
            Log.LogException(ex, "csCertificadosSAT.esCertificadoVigente")
            Return False
        Finally
            objCert = Nothing
        End Try
    End Function

    ''' <summary>
    ''' Funcion que verifica que la Llave Privada de CSD en pRutaKey sea desencriptada a través del password proporcionado pPwd
    ''' </summary>
    ''' <param name="pRutaKey">Ruta completa donde se encuentra la Llave privada del CSD (*.key)</param>
    ''' <param name="pPwd">Password que desencripta la Llave Privada de CSD</param>
    ''' <returns>
    ''' True: Si el Password logra desencriptar la Llave Privada
    ''' False: Si por alguna razón no se logró desencriptar la Llave Privada
    ''' </returns>
    ''' <remarks></remarks>
    Public Shared Function esPasswordCorrecto(ByVal pRutaOpenssl As String, ByVal pRutaKey As String, ByVal pPwd As String) As Boolean
        Dim esValido As Boolean = False
        Dim rutaPem As String = pRutaKey.Replace(Path.GetExtension(pRutaKey), ".pem")
        Dim strPem As String = ""
        Dim strError As String = ""
        Dim proc As Process
        Try
            proc = New Process()
            proc.StartInfo.FileName = Chr(34) & pRutaOpenssl & Chr(34)
            proc.StartInfo.Arguments = " pkcs8 -passin pass:" & pPwd & _
                                        " -inform DER -in " & Chr(34) & pRutaKey & Chr(34) & _
                                        " -out " & Chr(34) & rutaPem & Chr(34)
            proc.StartInfo.CreateNoWindow = True
            proc.StartInfo.UseShellExecute = False
            proc.StartInfo.ErrorDialog = False
            proc.Start()
            proc.WaitForExit()

            If (File.Exists(rutaPem)) Then
                strPem = csArchivos.leeArchivo(rutaPem, strError).Trim()
                If (strPem = "") Then
                    esValido = False
                Else
                    esValido = True
                End If
            End If

            If (File.Exists(rutaPem)) Then
                File.Delete(rutaPem)
            End If

            Return esValido
        Catch ex As Exception
            Log.LogException(ex, "csCertificadosSAT.esPasswordCorrecto")
            Return False
        Finally
            esValido = Nothing
            rutaPem = Nothing
            strPem = Nothing
            strError = Nothing
            proc = Nothing
        End Try
    End Function

    ''' <summary>
    ''' Función que verifica que el Certificado y la Llave Privada se correspondan entre sí, verificando que el modulus de ambos sea igual
    ''' </summary>
    ''' <param name="pRutaCer">Ruta completa del Certificado de Sello Digital (*.cer)</param>
    ''' <param name="pRutaKey">Ruta completa de la Llave Privada del Certificado de Sello Digital (*.key)</param>
    ''' <param name="ppwd">Contraseña que desencripta la Llave Privada del Sello Digital</param>
    ''' <returns>
    ''' True:  Si el Modulus del Certificado es igual al Modulus de la Llave Privada
    ''' False: En caso contrario, si ocurrió una excepcion, o la contraseña no pudo desencriptar la llave privada o los modulus no corresponden
    ''' </returns>
    ''' <remarks></remarks>
    Public Shared Function esKeyDeCert(ByVal pRutaOpenssl As String, ByVal pRutaCer As String, _
                                       ByVal pRutaKey As String, ByVal ppwd As String) As Boolean
        Dim start As ProcessStartInfo = New ProcessStartInfo()
        Dim validoKeyDeCert As Boolean = False
        Dim rutaPem As String = pRutaKey.Replace(Path.GetExtension(pRutaKey), ".pem")
        Dim modulusC As String = ""
        Dim modulusK As String = ""
        Dim esValidoKeyPem As Boolean = False
        Dim strPem As String = ""
        Dim strError As String = ""
        Try
            'Obtenemos el PEM del KEY
            start.FileName = Chr(34) & pRutaOpenssl & Chr(34)
            start.Arguments = " pkcs8 -passin pass:" & ppwd & _
                                        " -inform DER -in " & Chr(34) & pRutaKey & Chr(34) & _
                                        " -out " & Chr(34) & rutaPem & Chr(34)
            start.CreateNoWindow = True
            start.UseShellExecute = False
            start.ErrorDialog = False
            Using proc As Process = Process.Start(start)
                proc.WaitForExit()
            End Using

            If (File.Exists(rutaPem)) Then
                strPem = csArchivos.leeArchivo(rutaPem, strError).Trim()
                If (strPem = "") Then
                    esValidoKeyPem = False
                Else
                    esValidoKeyPem = True
                End If
            Else
                esValidoKeyPem = False
            End If

            If (esValidoKeyPem) Then
                'Obtenemos el Modulus del CER
                start.FileName = Chr(34) & pRutaOpenssl & Chr(34)
                start.Arguments = " x509 -inform DER -in " & _
                                    Chr(34) & pRutaCer & Chr(34) & " -noout -modulus"
                start.UseShellExecute = False
                start.RedirectStandardOutput = True
                start.WindowStyle = ProcessWindowStyle.Hidden
                start.CreateNoWindow = True

                Using proc As Process = Process.Start(start)
                    Using reader As StreamReader = proc.StandardOutput
                        modulusC = reader.ReadToEnd()
                    End Using
                End Using

                'Obtenemos el Modulos del Key
                start.Arguments = "rsa -in  " & _
                                    Chr(34) & rutaPem & Chr(34) & " -noout -modulus"
                Using proc As Process = Process.Start(start)
                    Using reader As StreamReader = proc.StandardOutput
                        modulusK = reader.ReadToEnd()
                    End Using
                End Using

                If (modulusC <> "" And modulusK <> "") Then
                    If (modulusC = modulusK) Then
                        validoKeyDeCert = True
                    Else
                        validoKeyDeCert = False
                    End If
                Else
                    validoKeyDeCert = False
                End If
            Else
                validoKeyDeCert = False
            End If

            If (File.Exists(rutaPem)) Then
                File.Delete(rutaPem)
            End If

            Return validoKeyDeCert
        Catch ex As Exception
            Log.LogException(ex, "csCertificadosSAT.esKeyDeCert")
            Return False
        Finally
            start = Nothing
            validoKeyDeCert = Nothing
            rutaPem = Nothing
            modulusC = Nothing
            modulusK = Nothing
            esValidoKeyPem = Nothing
            strPem = Nothing
            strError = Nothing
        End Try
    End Function

    ''' <summary>
    ''' Corre una batería de validaciones de certificados:
    ''' *Que se obtenga la Serie del Certificado de 20 caracteres
    ''' *Que el certificado sea vigente a la fecha actual
    ''' *Que la contraseña abra la Llave privada
    ''' *Que el certificado y la Llave privada se correspondan entre sí por medio del modulus
    ''' *Que se obtenga la información general del certificado así como el RFC a quien pertenece del Certificado.
    ''' </summary>
    ''' <param name="rutaCert">Ruta absoluta del Certificado de Sello Digital (*.cer)</param>
    ''' <param name="rutaKey">Ruta absoluta de la Llave privada del Certificado de Sello Digital (*.key)</param>
    ''' <param name="Pwd">Contraseña que descifra la Llave Privada del Certificado de Sello Digital</param>
    ''' <param name="strRfcSujeto">Parámetro por referencia para retornar el RFC a quien pertenece el Certificado</param>
    ''' <param name="strInfoCert">Parámetro por referencia para retornar la Información General del Certificado</param>
    ''' <param name="strNoSerieCertificado">Parámetro por referencia para retornar el número de serie del Certificado</param>
    ''' <param name="strResultado">Parámetro por referencia para retornar el mensaje del error en caso de que los certificados no pasaran alguna validación</param>
    ''' <returns>
    ''' True: Si los Certificados pasan todas las validaciones
    ''' False. Si los Certificados no pasan alguna validación
    ''' </returns>
    ''' <remarks>Es una validación tipo SWITCH donde se rompe el procesamiento al encontrar una validación fallida</remarks>
    Public Shared Function validaCertificados(ByVal rutaOpenssl As String, ByVal rutaCert As String, ByVal rutaKey As String, _
                                              ByVal fechaParaValidacion As Date, ByVal Pwd As String, ByRef strRfcSujeto As String,
                                              ByRef strNoSerieCertificado As String, ByRef fInicVig As Date, ByRef fFinVig As Date, _
                                              ByRef strResultado As String) As Boolean
        strRfcSujeto = ""
        strResultado = ""
        strNoSerieCertificado = ""
        fInicVig = New Date(1, 1, 1)
        fFinVig = New Date(1, 1, 1)
        Dim tipoCert As tipoCertificado = tipoCertificado.OTRO
        Dim boolIsValid As Boolean = False

        Try
            If (csArchivos.existeArchivo(rutaOpenssl, "exe") And _
            csArchivos.existeArchivo(rutaCert, "cer") And _
            csArchivos.existeArchivo(rutaKey, "key") And _
            Not (String.IsNullOrEmpty(Pwd))) Then
                'OBTENIENDO LOS DATOS GENERALES
                strNoSerieCertificado = getSerieCertificado(File.ReadAllBytes(rutaCert), fInicVig, fFinVig, strRfcSujeto)

                If (strNoSerieCertificado.Trim().Length = 20) And _
                    (strRfcSujeto.Trim.Length = 12 Or strRfcSujeto.Trim.Length = 13) And
                    (fInicVig.Date <> New Date(1, 1, 1)) And
                    (fFinVig.Date <> New Date(1, 1, 1)) Then
                    'Se obtuvieron datos, hacer validaciones:
                    tipoCert = getTipoCertificado(rutaCert)
                    If (strRfcSujeto.Length = 12 And tipoCert = tipoCertificado.CSD) Or _
                        (strRfcSujeto.Length = 13 And (tipoCert = tipoCertificado.CSD Or tipoCert = tipoCertificado.FIEL)) Then
                        If (esCertificadoVigente(rutaCert, fechaParaValidacion)) Then
                            If (esPasswordCorrecto(rutaOpenssl, rutaKey, Pwd)) Then
                                If (esKeyDeCert(rutaOpenssl, rutaCert, rutaKey, Pwd)) Then
                                    'En este punto el certificado es valido, retornar valores
                                    boolIsValid = True
                                Else
                                    strResultado &= "El Certificado no corresponde a la Llave Privada." & vbCrLf
                                End If
                            Else
                                strResultado &= "La Contraseña proporcionada no corresponde a la Llave Privada." & vbCrLf
                            End If
                        Else
                            strResultado &= "El Certificado proporcionado no es vigente al " & String.Format("{0:dd-MM-yyyy}", fechaParaValidacion) & vbCrLf
                        End If
                    Else
                        If (strRfcSujeto.Length = 12) Then
                            strResultado &= "El Certificado proporcionado corresponde a una FIEL, debe proporcionar un CSD (Persona Moral)." & vbCrLf
                        Else
                            strResultado &= "No se logró determinar el tipo de Certificado (Persona Fisica)." & vbCrLf
                        End If
                    End If
                Else
                    strResultado = "No se logró obtener la información básica del Certificado." & vbCrLf
                End If
            Else
                strResultado = "Verifique la información proporcionada, no existen los archivos de certificados o contraseña es vacía o no existen los archivos auxiliares." & vbCrLf
            End If

            Return boolIsValid
        Catch ex As Exception
            Log.LogException(ex, "csCertificadosSAT.validaCertificados")
            strResultado = "Ocurrió un error al verificar los Certificados de Sello Digital:" & vbCrLf & ex.Message
            Return False
        Finally
            tipoCert = Nothing
            boolIsValid = Nothing
        End Try
    End Function

    Public Shared Function convierteAPfx(ByVal pRutaOpenssl As String, ByVal rutaCert As String, ByVal rutaKey As String,
                                         ByVal pwdKey As String, ByRef strMsg As String) As Byte()
        Dim bytePfx As Byte()
        Dim boolEsValidCerPem As Boolean
        Dim boolEsValidKeyPem As Boolean
        Dim rutaPemCer As String = rutaCert.Replace(Path.GetExtension(rutaCert), "CER.pem")
        Dim rutaPemKey As String = rutaKey.Replace(Path.GetExtension(rutaKey), "KEY.pem")
        Dim rutaPfx As String = rutaKey.Replace(Path.GetExtension(rutaKey), ".pfx")
        Dim strPem As String = ""
        Dim strError As String = ""
        Dim proc As Process = New Process()
        Try
            bytePfx = Nothing
            boolEsValidCerPem = False
            boolEsValidKeyPem = False
            proc = New Process()
            proc.StartInfo.FileName = Chr(34) & pRutaOpenssl & Chr(34)
            'Obtener el pem del Certificado:
            proc.StartInfo.Arguments = " x509 -inform DER -in " & Chr(34) & rutaCert & Chr(34) & _
                                        " -out " & Chr(34) & rutaPemCer & Chr(34)
            proc.StartInfo.CreateNoWindow = True
            proc.StartInfo.UseShellExecute = False
            proc.StartInfo.ErrorDialog = False
            proc.Start()
            proc.WaitForExit()

            If (File.Exists(rutaPemCer)) Then
                strPem = csArchivos.leeArchivo(rutaPemCer, strError).Trim()
                If (strPem = "") Then
                    boolEsValidCerPem = False
                    strMsg &= strError & vbCrLf
                Else
                    boolEsValidCerPem = True
                End If
            End If

            'Obtener el pem del key
            proc.StartInfo.Arguments = " pkcs8 -passin pass:" & pwdKey & _
                                        " -inform DER -in " & Chr(34) & rutaKey & Chr(34) & _
                                        " -out " & Chr(34) & rutaPemKey & Chr(34)
            proc.StartInfo.CreateNoWindow = True
            proc.StartInfo.UseShellExecute = False
            proc.StartInfo.ErrorDialog = False
            proc.Start()
            proc.WaitForExit()

            If (File.Exists(rutaPemKey)) Then
                strPem = csArchivos.leeArchivo(rutaPemKey, strError).Trim()
                If (strPem = "") Then
                    boolEsValidKeyPem = False
                    strMsg &= strError & vbCrLf
                Else
                    boolEsValidKeyPem = True
                End If
            End If

            If (boolEsValidCerPem And boolEsValidKeyPem) Then
                'Generar el PFX
                proc.StartInfo.Arguments = " pkcs12 -export -out " & Chr(34) & rutaPfx & Chr(34) & _
                                            " -inkey " & Chr(34) & rutaPemKey & Chr(34) & _
                                            " -in " & Chr(34) & rutaPemCer & Chr(34) & _
                                            " -passout pass:" & pwdKey
                proc.StartInfo.CreateNoWindow = True
                proc.StartInfo.UseShellExecute = False
                proc.StartInfo.ErrorDialog = False
                proc.Start()
                proc.WaitForExit()

                If (File.Exists(rutaPfx)) Then
                    strPem = csArchivos.leeArchivo(rutaPfx, strError).Trim()
                    If (strPem = "") Then
                        bytePfx = Nothing
                        strMsg &= strError & vbCrLf
                    Else
                        'Cargar el pfx como byte
                        bytePfx = csArchivos.leeArchivoEnByteArray(rutaPfx)
                    End If
                End If
            End If

            'borra los archivos generados
            If (File.Exists(rutaPemCer)) Then
                File.Delete(rutaPemCer)
            End If
            If (File.Exists(rutaPemKey)) Then
                File.Delete(rutaPemKey)
            End If
            If (File.Exists(rutaPfx)) Then
                File.Delete(rutaPfx)
            End If

            Return bytePfx
        Catch ex As Exception
            Log.LogException(ex, "csCertificadosSAT.convierteAPfx")
            strMsg &= "Ocurrió un error al generar PFX: " & ex.Message & vbCrLf
            Return Nothing
        Finally
            bytePfx = Nothing
            boolEsValidCerPem = Nothing
            boolEsValidKeyPem = Nothing
            rutaPemCer = Nothing
            rutaPemKey = Nothing
            rutaPfx = Nothing
            strPem = Nothing
            strError = Nothing
            proc.Close()
            proc.Dispose()
        End Try
    End Function
End Class
